package com.example.userservice.controller;

import com.example.userservice.apollo.TestApolloAnnotationBean;
import com.example.userservice.config.AppConfig;
import com.example.userservice.servicedomain.UserDomain;
import com.example.userservice.services.UserOhterService;
import com.example.userservice.services.UserService;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
import io.netty.util.concurrent.DefaultEventExecutorGroup;
import io.netty.util.concurrent.EventExecutorGroup;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.FutureListener;
import org.apache.commons.lang.RandomStringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.PostConstruct;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.*;
import java.util.stream.Collectors;

import static java.lang.String.format;
import static java.util.stream.Collectors.toList;

@RestController
@RequestMapping("/user")
public class UserController {

    private final UserService userService;

    private final UserOhterService userOhterService;

    @Autowired
    AppConfig appConfig;

    @Value("${app.id}")
    String appid;

    @Value("${server.port}")
    String port;

    @PostConstruct
    public void aUserController(){
        System.out.print("UserController加载了");
    }

//    @Value("${params.poolsize}")
//    private Integer poolSize;
//
//    @Value("${params.sendtopic.TP_GUN_STATUS}")
//    private String TP_GUN_STATUS;
//
//    @Value("${user-service.ribbon.ConnectTimeout}")
//    private String plus;

    //https://www.cnblogs.com/coder-lichao/p/10931919.html
    public static final int PROCESSORS=Runtime.getRuntime().availableProcessors();

    //线程最大的空闲存活时间，单位为秒
    public static final int KEEPALIVETIME=60;

    //任务缓存队列长度
    public static final int BLOCKINGQUEUE_LENGTH=500;

    public final static  ThreadPoolExecutor  te = new ThreadPoolExecutor(PROCESSORS * 2,PROCESSORS * 4,KEEPALIVETIME,TimeUnit.SECONDS,
        new ArrayBlockingQueue<Runnable>(BLOCKINGQUEUE_LENGTH));


    ExecutorService es2 = Executors.newFixedThreadPool(2);

    private final Executor executor = Executors.newFixedThreadPool(Math.min(8, 100), new ThreadFactory() {
        @Override
        public Thread newThread(Runnable r) {
            System.out.print(" 拿到的线程："+Thread.currentThread().getId());
            Thread t = new Thread(r);
            t.setDaemon(true);
            return t;
        }
    });

    public UserController(UserService userService, UserOhterService userOhterService) {
        this.userService = userService;
        this.userOhterService = userOhterService;
    }

    @GetMapping("/hi")
    public String hi(@RequestParam("name") String name) {
        System.out.print(appid);
        System.out.print(port);
        return "hi";
        //return userService.sayHi(name);
    }

    @GetMapping("testConfig")
    public String testConfig(){
        TestApolloAnnotationBean testApolloAnnotationBean =appConfig.testApolloAnnotationBean();
        int batch =testApolloAnnotationBean.getBatch();
        return "x";
    }


    //ts  three second 延迟
    @GetMapping("/tshi")
    public String tshi(@RequestParam String name) {

        return userService.sayHi(name);
    }

    @GetMapping("/goon")
    public String goon(@RequestParam String json) {

        return userService.goon(json);
    }


    @GetMapping("/mainHi")
    public String mainHi() {
        //串行时间测试
        Long f1 = System.currentTimeMillis();
        String a = userService.sayHi("a");
        String b = userOhterService.sayOhterHi("b");

        String a2 = userService.sayHi("a");
        String b2 = userOhterService.sayOhterHi("b");
        Long f2 = System.currentTimeMillis();
        System.out.println("在回调的回调中耗时..." + (f2 - f1));
        return String.valueOf(f2 - f1);
    }


    @GetMapping("/otherFunSayHai")
    public String otherFunSayHai() {
        userService.otherFunSayHai("z");
        return "z";
    }

    @GetMapping("/asyncGetPprice")
    public String asyncGetPprice() {

        //completableFuture2.join(completableFuture.ge);
        String result1 = CompletableFuture.supplyAsync(() -> userService.sayHi("c"), executor).thenApplyAsync(v -> v + "world").join();
        String result2 = CompletableFuture.supplyAsync(() -> userService.sayHi("c"), executor).thenApplyAsync(v -> v + "world").join();
        System.out.println(result1);
        return result1;
    }

    //todo
    @GetMapping("nettyHi")
    public String nettyHi() {
        long l = System.currentTimeMillis();
        EventExecutorGroup group = new DefaultEventExecutorGroup(4); // 4 threads
        io.netty.util.concurrent.Future<Integer> f = group.submit(new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                System.out.println("执行耗时操作...");
                String a = userService.sayHi("a");
                System.out.println("nettycall:" + (System.currentTimeMillis() - l) + " ms");
                return 100;
            }
        });

        f.addListener(new FutureListener<Object>() {
            @Override
            public void operationComplete(Future<Object> objectFuture) throws Exception {
                System.out.println("计算结果:：" + objectFuture.get());
                System.out.println("listener:" + (System.currentTimeMillis() - l) + " ms");
            }
        });

        //////  第二个任务
        EventExecutorGroup group2 = new DefaultEventExecutorGroup(4); // 4 threads
        io.netty.util.concurrent.Future<Integer> f2 = group.submit(new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                System.out.println("执行耗时操作...");
                String a = userOhterService.sayOhterHi("b");
                System.out.println("nettycall2:" + (System.currentTimeMillis() - l) + " ms");
                return 200;
            }
        });

        f2.addListener(new FutureListener<Object>() {
            @Override
            public void operationComplete(Future<Object> objectFuture) throws Exception {
                System.out.println("计算结果2:：" + objectFuture.get());
                System.out.println("listener2:" + (System.currentTimeMillis() - l) + " ms");
            }
        });


        System.out.println("主线程运算耗时:" + (System.currentTimeMillis() - l) + " ms");


        return "";
    }

    //todo
    @GetMapping("guavaHi")
    public String guavaHi() throws Exception {
        Long f1 = System.currentTimeMillis();
        System.out.println("-------------------------------- 神秘的分割线1 -----------------------------------");
        // 好的实现应该是提供回调,即异步调用完成后,可以直接回调.本例采用guava提供的异步回调接口,方便很多.
        ListeningExecutorService guavaExecutor = MoreExecutors
                .listeningDecorator(Executors.newSingleThreadExecutor());

        final ListenableFuture<String> listenableFuture = guavaExecutor
                .submit(new Callable<String>() {

                    @Override
                    public String call() throws Exception {
                        String a = userService.sayHi("a");
                        //return  "[" + Thread.currentThread().getName() +"]: guava的Future返回结果";
                        return "a";
                    }
                });

        // 注册监听器,即异步调用完成时会在指定的线程池中执行注册的监听器
        listenableFuture.addListener(new Runnable() {
            @Override
            public void run() {
                try {
                    String logTxt = "[" + Thread.currentThread().getName() + "]: guava对返回结果进行异步CallBack(Runnable):"
                            + listenableFuture.get();
                    Long f22 = System.currentTimeMillis();
                    System.out.println("echo1" + logTxt + "实际耗时:" + (f22 - f1));


                } catch (Exception e) {
                }
            }
        }, Executors.newSingleThreadExecutor());


        //////----
        ListeningExecutorService guavaExecutor2 = MoreExecutors
                .listeningDecorator(Executors.newSingleThreadExecutor());

        final ListenableFuture<String> listenableFuture2 = guavaExecutor
                .submit(new Callable<String>() {

                    @Override
                    public String call() throws Exception {
                        String b = userOhterService.sayOhterHi("b");
                        //return  "[" + Thread.currentThread().getName() +"]: guava的Future返回结果";
                        return "b";
                    }
                });

        // 注册监听器,即异步调用完成时会在指定的线程池中执行注册的监听器
        listenableFuture.addListener(new Runnable() {
            @Override
            public void run() {
                try {
                    String logTxt = "[" + Thread.currentThread().getName() + "]: guava对返回结果进行异步CallBack(Runnable):"
                            + listenableFuture2.get();
                    Long f22 = System.currentTimeMillis();
                    System.out.println("echo2" + logTxt + "实际耗时:" + (f22 - f1));


                } catch (Exception e) {
                }
            }
        }, Executors.newSingleThreadExecutor());


        // 主线程可以继续执行,异步完成后会执行注册的监听器任务.
        System.out.println("[" + Thread.currentThread().getName() + "]: guavaFuture1执行结束");
        Long f2 = System.currentTimeMillis();
        System.out.println("guava主线程耗时..." + (f2 - f1));
        return "guava";
    }


    @GetMapping("/mianHiCon2")
    public String mainHiCon2() {
        long startTime = System.currentTimeMillis();
        CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> userService.sayHi("a"), es2);

        CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> userService.sayHi("a"), es2);

        CompletableFuture<String> future3 = CompletableFuture.supplyAsync(() -> userService.sayHi("a"), es2);

        CompletableFuture<String> future4 = CompletableFuture.supplyAsync(() -> userService.sayHi("a"), es2);

        CompletableFuture<String> future5 = CompletableFuture.supplyAsync(() -> userService.sayHi("a"), es2);

        CompletableFuture<String> future6 = CompletableFuture.supplyAsync(() -> userService.sayHi("a"), es2);

        CompletableFuture.allOf(future, future2, future3, future4, future5, future6).whenCompleteAsync(
                (v, e) -> {
                    System.err.print("---");
                    //System.err.println(data);
                    String s1 = future.join();
                    String s2 = future2.join();
                    String s3 = future3.join();
                    String s4 = future4.join();
                    String s5 = future5.join();
                    String s6 = future6.join();
                    System.err.println("结束:" + s1 + s2 + s3 + "结束");
                    System.err.println("总耗时：" + (System.currentTimeMillis() - startTime));
                }
        );
        return null;
    }


    @GetMapping("/mainHiCon")
    public String mainHiCon() {
        long startTime = System.currentTimeMillis();
        List<String> data = new ArrayList<>();
        CompletableFuture<Void> cf1 = CompletableFuture.runAsync(() -> {
            System.out.println("1:" + Thread.currentThread().getName());
            //String b = userOhterService.sayOhterHi("b");
            data.add("a");
        }, es2);

        //todo  CompletableFuture<U>  必须是 supplyAsybc 支持

        //Shop 为什么是
        //Future<String>
        //Future<Double>


        CompletableFuture<Void> cf2 = CompletableFuture.runAsync(() -> {
            System.out.println("1:" + Thread.currentThread().getName());
            String b = userOhterService.sayOhterHi("b");
            data.add(b);
        }, es2);

        CompletableFuture<Void> cf11 = CompletableFuture.runAsync(() -> {
            System.err.println(Thread.currentThread().getName());
            String a = userService.sayHi("a");
            data.add(a);
        }, es2);


        CompletableFuture<Void> cf22 = CompletableFuture.runAsync(() -> {
            System.out.println("1:" + Thread.currentThread().getName());
            String b = userOhterService.sayOhterHi("b");
            data.add(b);
        }, es2);


        CompletableFuture<Void> cf31 = CompletableFuture.runAsync(() -> {
            System.err.println(Thread.currentThread().getName());
            String a = userService.sayHi("a");
            data.add(a);
        }, es2);


        CompletableFuture<Void> cf32 = CompletableFuture.runAsync(() -> {
            System.out.println("1:" + Thread.currentThread().getName());
            String b = userOhterService.sayOhterHi("b");
            data.add(b);
        }, es2);


        CompletableFuture.allOf(cf1, cf2, cf11, cf22, cf31, cf32)
                .whenCompleteAsync((v, e) -> {
                            System.err.print("---");
                            System.err.println(data);
                            System.err.println("结束");
                            System.err.println("总耗时：" + (System.currentTimeMillis() - startTime));
                        }
                );
        System.err.println("外部总耗时：" + (System.currentTimeMillis() - startTime));
        //todo1
        return "";
    }


    @GetMapping("/mapPrice1")
    public void mapPrice1() {
        List<UserDomain> shops = Arrays.asList(new UserDomain(), new UserDomain());
        long startTime = System.currentTimeMillis();
        List<String> needList = shops.parallelStream().map(shop -> shop.getHai("a")).collect(Collectors.toList());
        System.err.println("map外部总耗时：" + (System.currentTimeMillis() - startTime));
    }

    @GetMapping("/mapPrice2")
    public void mapPrice2() {
        List<UserDomain> shops = Arrays.asList(new UserDomain(), new UserDomain());
        long startTime = System.currentTimeMillis();
        List<CompletableFuture<String>> needList = shops.stream()
                .map(shop -> CompletableFuture.supplyAsync(() -> shop.getHai("a")))
                .collect(Collectors.toList());

        needList.stream().map(CompletableFuture::join).collect(toList());

        System.err.println("mapPrice2外部总耗时：" + (System.currentTimeMillis() - startTime) + "长度：" + needList.size());

    }

    //这个版本是最好的 但是不会玩
    @GetMapping("/mapPrice3")
    public void mapPrice3() {
        //关于批量
        long startTime = System.currentTimeMillis();
        List<Object> objs = Arrays.asList(new String("1"),new String("2"));
        List<UserDomain> shops = Arrays.asList(new UserDomain(),
                new UserDomain(),
                new UserDomain(),
                new UserDomain(),
                new UserDomain(),
                new UserDomain(),
                new UserDomain(),
                new UserDomain())
        ;
        //Arrays.asList(new Integer[10]).stream()

        List<CompletableFuture<String>> priceFutures =
                shops.stream().map(shop -> CompletableFuture.supplyAsync(() -> {
                                    System.out.print("取的线程:"+Thread.currentThread().getId());
                                    return shop.getHai("a");
                                }, es2
                                )
                        )
                        .collect(Collectors.toList());

        List<String> prices = priceFutures.stream()
                .map(CompletableFuture::join)
                .collect(Collectors.toList());


        System.err.println("mapPrice3外部总耗时：" + (System.currentTimeMillis() - startTime) + "长度：" + prices.size());
        //todo 第一 steam的线程数
        //todo 第二 合并是否要一样的服务 例如 shop 和 user 合并 而不是 shop 和 shop 合并
        //todo  check 的对象  list<CompletableFuture<Obj>>  ?? 不用string 用 父类 FUObj 可以不

    }


    @GetMapping("/testZipKin")
    public String testZipKin(@RequestParam("name") String name) {
        HashMap<String, String> in = new HashMap<>();
        in.put("name", name);
        return userService.testZipKin(in);
    }

//

    //    //todo 看 能否合并 User
//    @GetMapping("/hebing")
//    public String hebing(){
//        //todo 第一要合并流程
//        //todo 第二要分别解析两次结果
//        String name1 = "name1";
//        //shops.约等于service
//        //List<Shop>   List<UserService>
//        List<UserService> xshops = new ArrayList<>();
//        Executor executor = Executors.newFixedThreadPool(100);
//        List<CompletableFuture<UserService>> priceFuture = xshops.stream().map((UserService userService) -> CompletableFuture.supplyAsync(()-> userService.sayHi(name1)), executor).collect(Collectors.toList());
//        List<Object> list = priceFuture.stream().map(CompletableFuture::join).collect(Collectors.toList());
//        return null;
//    }

    //并行shop的price
    @GetMapping("shopPrice")
    public String shopPrice() {
        // batch userid 1 2 3 4
        //todo 1 并行两个相同方法是否要实现 例如 4个CPU 取用户就同时放4个连接进来 base线getUser 并行拿
        // 拿到结果 。可能结果还在路上。例如是否封号不知道
        //todo 2 同时拿4个用户的钱 uid 1 2 3 4 的钱
        //批量uid 的用户  和 批量uid 的钱 都拿到了后 在合并
        //那是否要一起给桩。那就不一定了
        //哪个先有结果。就启动哪个桩呗
        return null;
    }

    //并行两个业务？例如 拿一个用户(不知道是否封号) ，同时拿钱() 再合并结果？
    @GetMapping("shopUserAndMoney")
    public String shopUserAndMoney() {
        return null;
    }

    @GetMapping("demo")
    public String demo() {

        return null;
    }

    @GetMapping("/demo2")
    public String demo2() {
        if (te.getQueue().size() > 100) {

            System.out.print("[告警"+te.getQueue().size()+"]");
        }
        System.out.print("ok-BEGIN");
        te.execute(new Runnable() {
            @Override
            public void run() {
                try {
                    TimeUnit.SECONDS.sleep(4);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.print("demo2");
            }
        });
        System.out.print("ok-EDN");
        return "ok";
    }



    public double calculatePrice(String product) {

        return 12.1;
    }

    public static void main(String[] args) throws InterruptedException {
        //教程https://blog.csdn.net/jjlovefj/article/details/82756443
        //https://blog.csdn.net/sweatOtt/article/details/83154808
        String blackWhiteFlag = "0";
        System.out.print(System.getProperty("a"));
        System.out.print(System.getenv("b"));
        System.out.print(System.getenv("JAVA_HOME"));
//        System.out.print(appid);
//        System.out.print(port);
//        if (!StringUtils.isEmpty(blackWhiteFlag) && blackWhiteFlag.equals("1")) {
//
//            System.out.println(0);
//        }else{
//            System.out.println(1);
//        }

        String terminalId ="3202";
        String time = new SimpleDateFormat("yyyyMMddHHmmssSS").format(new Date());
        String flowId;
        System.out.println(time);
        System.out.println(time.length());
//        for (int i= 0;i<1000;i++) {
//            Thread.sleep(101);
//            System.out.println(time.length());
//        }
//
        if (time.length() == 17) {
            System.out.println(time);
            flowId = terminalId + time.substring(0, time.length() - 1);
        } else {
            flowId = terminalId + time;
        }
        System.out.println(flowId);

        //String terminalId2 = "3201060001486901";
        //String t           = "3201060001609001,"
        String terminalId2 =   "3201060001633701";
        String time2 = new  SimpleDateFormat("yyMMddHHmmssSS").format(new Date());
        String ff = terminalId2 + time2 + "0";
        System.out.println("长度");
        System.out.println(ff.length());
        if(ff.length()>32){
            flowId = ff.substring(0,32);
        }else if(ff.length() == 32){
            flowId = ff;
        }else{
            flowId = ff + getRandomString(32- ff.length());
        }

        System.out.println("流水号：");
        System.out.println(flowId);
        System.out.println("长度"+flowId.length());
    }

    public static String getRandomString(int length){
        //定义一个字符串（A-Z，a-z，0-9）即62位；
        String str="01234567890123456789012345678901234567890123456789012345678901";
        //由Random生成随机数
        Random random=new Random();
        StringBuffer sb=new StringBuffer();
        //长度为几就循环几次
        for(int i=0; i<length; ++i){
            //产生0-61的数字
            int number=random.nextInt(62);
            //将产生的数字通过length次承载到sb中
            sb.append(str.charAt(number));
        }
        //将承载的字符转换成字符串
        return sb.toString();
    }





}
